﻿using Crossroad.Utils.Helper;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace Crossroad.Utils.Extensions
{
    /// <summary>
    /// 字符串类型扩展
    /// 编码：陈冉
    /// 2018-2-27 13:59:32
    /// </summary>
    public static class StringExt
    {
        /// <summary>
        /// 转bool类型
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool ToBool(this string input)
        {
            return !input.IsNullOrEmpty() && input.ToLower().Equals("true");
        }

        /// <summary>
        /// 转int类型
        /// </summary>
        /// <returns></returns>
        public static int ToInt(this string input)
        {
            return int.TryParse(input, out var result) ? result : 0;
        }

        /// <summary>
        /// 转int?类型
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static int? ToIntNon(this string input)
        {
            if (input == null)
                return null;

            return ToInt(input);
        }

        /// <summary>
        /// 转short类型
        /// </summary>
        /// <returns></returns>
        public static short ToShort(this string input)
        {
            return short.TryParse(input, out var result) ? result : (short)0;
        }

        /// <summary>
        /// 转short?类型
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static short? ToShortNon(this string input)
        {
            if (input == null)
                return null;

            return ToShort(input);
        }

        /// <summary>
        /// 转long类型
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static long ToLong(this string input)
        {
            return long.TryParse(input, out var result) ? result : 0;
        }

        /// <summary>
        /// 转long?类型
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static long? ToLongNon(this string input)
        {
            if (input == null)
                return null;

            return ToLong(input);
        }

        /// <summary>
        /// 转DateTime类型
        /// </summary>
        /// <returns></returns>
        public static DateTime ToDateTime(this string input)
        {
            return DateTime.TryParse(input, out var result) ? result : DateTime.Now;
        }

        /// <summary>
        /// 转decimal类型
        /// </summary>
        /// <returns></returns>
        public static decimal ToDecimal(this string input)
        {
            return decimal.TryParse(input, out var result) ? result : 0;
        }

        /// <summary>
        /// 转double类型
        /// </summary>
        /// <returns></returns>
        public static double ToDouble(this string input)
        {
            return double.TryParse(input, out var result) ? result : 0;
        }

        /// <summary>
        /// 转float类型
        /// </summary>
        /// <returns></returns>
        public static float ToFloat(this string input)
        {
            return float.TryParse(input, out var result) ? result : 0;
        }

        /// <summary>
        /// 转数据库表名 例：TableName => table_name
        /// </summary>
        /// <param name="input"></param>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public static string ToMapperName(this string input, string prefix = "")
        {
            prefix = prefix.IsEmpty() ? "" : prefix;
            return prefix + Regex.Replace(input, @"[A-Z]", (m) => m.Index > 0 ? "_" + m.Value.ToLower() : m.Value.ToLower());
        }

        /// <summary>
        /// 是否是空
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsEmpty(this string input)
        {
            return (input ?? "").Equals(string.Empty);
        }

        /// <summary>
        ///  是否是空或NULL
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string input)
        {
            return string.IsNullOrEmpty(input);
        }

        /// <summary>
        /// 是否无内容
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool NotContent(this string input)
        {
            return string.IsNullOrEmpty(input);
        }

        /// <summary>
        /// 是否有内容
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool Contains(this string input)
        {
            return !string.IsNullOrEmpty(input);
        }

        /// <summary>
        /// 正则表达式 拆分数据
        /// </summary>
        /// <param name="input"></param>
        /// <param name="regexStr"></param>
        /// <returns></returns>
        public static string[] RegSplit(this string input, string regexStr)
        {
            return Regex.Split(input, regexStr);
        }

        /// <summary>
        /// 日期字符串填充 一天起始的时分秒
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string FillStartTime(this string input)
        {
            return $"{input} 00:00:00";
        }

        /// <summary>
        /// 日期字符串填充 一天结束的时分秒
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string FillEndTime(this string input)
        {
            return $"{input} 23:59:59";
        }

        /// <summary>
        /// 底划线格式转驼峰格式
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>        
        public static string ToCamelCase(this string input)
        {
            var parts = input
                .Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);

            return $"{parts.First().ToLower()}{string.Join("", parts.Skip(1).Select(ToCapital))}";
        }

        /// <summary>
        /// 驼峰转下划线格式
        /// </summary>
        /// <param name="input"></param>
        /// <param name="acronymUpper">首字母是否大写 默认：否</param>
        /// <returns></returns>
        public static string ToUnderline(this string input, bool acronymUpper = false)
        {
            if (input.IsEmpty())
                return string.Empty;

            StringBuilder sb = new StringBuilder();

            foreach (var c in input.ToArray())
            {
                if (char.IsUpper(c))
                {
                    sb.Append("_");
                    sb.Append(char.ToLower(c));
                }
                else
                {
                    sb.Append(c);
                }
            }
            var opt = sb.ToString();
            if (opt.IndexOf("_") == 0)
                opt = opt.Substring(1, opt.Length - 1);
            if (acronymUpper)
                opt = opt.FirstLetterUpper();

            return opt;
        }

        /// <summary>
        /// 首字母大写，其他字符小写
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ToCapital(this string input)
        {
            return $"{char.ToUpper(input[0])}{input.Substring(1).ToLower()}";
        }

        /// <summary>
        /// 首字母大写
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string FirstLetterUpper(this string input)
        {
            return $"{char.ToUpper(input[0])}{input.Substring(1)}";
        }

        /// <summary>
        /// json字符串转对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="enableUnderLine">是否启用底划线转驼峰</param>
        /// <returns></returns>        
        public static T ToEntity<T>(this string input, bool enableUnderLine = true)
        {
            if (!enableUnderLine)
                return JsonConvert.DeserializeObject<T>(input);

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy { ProcessDictionaryKeys = true }
                },
                Formatting = Formatting.Indented
            };

            return JsonConvert.DeserializeObject<T>(input, settings);
        }


        /// <summary>
        /// json字符串转对象
        /// </summary>
        /// <param name="input"></param>
        /// <param name="enableUnderLine">是否启用底划线转驼峰 默认:启用</param>
        /// <returns></returns>
        public static object ToEntity(this string input, bool enableUnderLine = true)
        {
            if (!enableUnderLine)
                return JsonConvert.DeserializeObject(input);

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new UnderlineSplitContractResolver()
            };

            return JsonConvert.DeserializeObject(input, settings);
        }


        /// <summary>
        /// json字符串转对象
        /// </summary>
        /// <param name="input"></param>
        /// <param name="type"></param>
        /// <param name="enableUnderLine">是否启用底划线转驼峰</param>
        /// <returns></returns>
        public static object ToEntity(this string input, Type type, bool enableUnderLine = true)
        {
            if (!enableUnderLine)
                return JsonConvert.DeserializeObject(input, type);

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new UnderlineSplitContractResolver()
            };

            return JsonConvert.DeserializeObject(input, type, settings);
        }

        /// <summary>
        /// 字符串转对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="splitChar">参数名与参数值分隔符 默认':' 例：name:value</param>
        /// <returns></returns>
        public static T ToObject<T>(this string input, char splitChar = ':')
        {
            var type = typeof(T);
            var attrs = Regex.Matches(input, $@"(\w+){splitChar}(\w+)");
            var target = Activator.CreateInstance<T>();
            foreach (Match attr in attrs)
            {
                var field = attr.Value.Split(splitChar);
                var property = type.GetProperty(field[0]);
                if (property == null || field[1].IsEmpty())
                    continue;

                property.SetValue(target, Convert.ChangeType(field[1], property.PropertyType), null);
            }

            return target;
        }

        /// <summary>
        /// json字符串转换到匿名对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="anonymousTypeObject"></param>
        /// <param name="enableResolver"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this string input, T anonymousTypeObject, bool enableResolver = false)
        {
            if (!enableResolver)
                return JsonConvert.DeserializeAnonymousType(input, anonymousTypeObject);

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new UnderlineSplitContractResolver()
            };

            return JsonConvert.DeserializeAnonymousType(input, anonymousTypeObject, settings);
        }

        /// <summary>
        ///  字符串转换为指定对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="anonymousTypeObject"></param>
        /// <param name="splitChar"></param>
        /// <returns></returns>
        public static T ToObject<T>(this string input, T anonymousTypeObject, char splitChar = ':')
        {
            var type = anonymousTypeObject.GetType();
            var attrs = Regex.Matches(input, $@"(\w+){splitChar}(\w+)");
            var props = new Dictionary<string, object>();
            foreach (Match attr in attrs)
            {
                var field = attr.Value.Split(splitChar);
                var property = type.GetProperty(field[0]);
                if (property == null || field[1].IsEmpty())
                    continue;
                props[field[0]] = Convert.ChangeType(field[1], property.PropertyType);
            }

            return props.ToJson().ToEntity(anonymousTypeObject);
        }

        /// <summary>
        /// 获取UTF8字节数组
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static byte[] GetBytes(this string input)
        {
            return Encoding.UTF8.GetBytes(input);
        }

        #region 字符串分割处理

        /// <summary>
        /// 查询指定字符在当前字符串中出现的次数
        /// </summary>
        /// <param name="input"></param>
        /// <param name="vchar"></param>
        /// <returns></returns>
        public static int AppearaCount(this string input, char vchar)
        {
            if (!input.Contains(vchar))
                return 0;

            var count = 0;
            foreach (var c in input.ToCharArray())
            {
                if (c == vchar)
                    count++;
            }

            return count;
        }

        /// <summary>
        /// 删除最后结尾的一个逗号
        /// </summary>
        public static string DelLastComma(this string input)
        {
            if (input.IsNullOrEmpty())
                return string.Empty;

            return input.LastIndexOf(',') == input.Length - 1 ? input.Substring(0, input.LastIndexOf(",", StringComparison.Ordinal)) : "";
        }

        /// <summary>
        /// 删除最后结尾的指定字符后的字符
        /// </summary>
        /// <param name="input"></param>
        /// <param name="vchar">指定符号 默认','</param>
        /// <returns></returns>
        public static string DelLastChar(this string input, char vchar = ',')
        {
            if (string.IsNullOrEmpty(input))
                return "";

            return input.LastIndexOf(vchar) == input.Length - 1 ? input.Substring(0, input.LastIndexOf(vchar)) : "";
        }

        /// <summary>
        /// 删除字符串两头的指定字符
        /// </summary>
        /// <param name="input"></param>
        /// <param name="vchar">指定符号 默认','</param>
        /// <returns></returns>
        public static string DelBothChar(this string input, char vchar = ',')
        {
            if (string.IsNullOrEmpty(input))
                return "";

            var arr = input.ToCharArray();
            if (arr[0] == vchar)
                input = input.Substring(1, input.Length - 1);
            if (arr[arr.Length - 1] == vchar)
                input = input.Substring(0, input.Length - 1);

            return input;
        }

        /// <summary>
        /// 拆分成List<string>
        /// </summary>
        /// <param name="input"></param>
        /// <param name="vchar"></param>
        /// <returns></returns>
        public static List<string> SplitStrList(this string input, char vchar = ',')
        {
            List<string> list = input.DelBothChar()
                .Split(new char[] {vchar}, StringSplitOptions.RemoveEmptyEntries)
                .Select(s => s).ToList();

            return list;
        }

        #endregion

        #region ========加密========

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Encrypt(this string input)
        {
            return input.Encrypt("cross^road");
        }
        /// <summary> 
        /// 加密数据 
        /// </summary> 
        /// <param name="Text"></param> 
        /// <param name="sKey"></param> 
        /// <returns></returns> 
        public static string Encrypt(this string input, string sKey)
        {
            var des = new DESCryptoServiceProvider();
            var inputByteArray = Encoding.Default.GetBytes(input);
            des.Key = Encoding.UTF8.GetBytes(sKey.ToMd5().Substring(0, 8));
            des.IV = Encoding.UTF8.GetBytes(sKey.ToMd5().Substring(0, 8));
            var ms = new System.IO.MemoryStream();
            var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            var ret = new StringBuilder();
            foreach (var b in ms.ToArray())
            {
                ret.AppendFormat("{0:X2}", b);
            }
            return ret.ToString();
        }
        #endregion

        #region ========解密========

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Decrypt(this string input)
        {
            return Decrypt(input, "cross^road");
        }

        public static string Decrypt(this string input, string sKey)
        {
            var des = new DESCryptoServiceProvider();
            var len = input.Length / 2;
            var inputByteArray = new byte[len];
            int x;
            for (x = 0; x < len; x++)
            {
                var i = Convert.ToInt32(input.Substring(x * 2, 2), 16);
                inputByteArray[x] = (byte)i;
            }
            des.Key = Encoding.UTF8.GetBytes(sKey.ToMd5().Substring(0, 8));
            des.IV = Encoding.UTF8.GetBytes(sKey.ToMd5().Substring(0, 8));
            var ms = new System.IO.MemoryStream();
            var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();

            return Encoding.Default.GetString(ms.ToArray());
        }

        #endregion

        #region ========辅助========
        public static string ToMd5(this string str)
        {
            var value = string.Empty;
            using (var mD5CryptoServiceProvider = new MD5CryptoServiceProvider())
            {
                var result = BitConverter.ToString(mD5CryptoServiceProvider.ComputeHash(Encoding.UTF8.GetBytes(str))).Replace("-", "").ToLower();
                return result.Where((t, i) => i % 2 == 0).Aggregate(value, (current, t) => current + t.ToString());
            }
        }
        #endregion
        
    }
}